// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @file HelloWorldPublisher.cpp
 *
 */
#include "MessagePubSubTypes.h"
#include "MessagePublisher.h"
//#include <fastrtps/participant/Participant.h>
//#include <fastrtps/attributes/ParticipantAttributes.h>
//#include <fastrtps/attributes/PublisherAttributes.h>
//#include <fastrtps/publisher/Publisher.h>
//#include <fastrtps/Domain.h>

#include <thread>

#include "logger.h"

HelloWorldPublisher::HelloWorldPublisher()
        : m_participant(nullptr)
        , m_publisher(nullptr)
        , m_sampleType(new HelloWorldPubSubType())
{
}

bool HelloWorldPublisher::init()
{
#if 0
    m_Hello.index(0);
    m_Hello.message("HelloWorld");
    ParticipantAttributes PParam;
    PParam.rtps.builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE;
    PParam.rtps.builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true;
    PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true;
    PParam.rtps.builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true;
    PParam.rtps.builtin.discovery_config.leaseDuration = c_TimeInfinite;
    PParam.rtps.setName("Participant_pub");
    mp_participant = Domain::createParticipant(PParam);

    if (mp_participant == nullptr)
    {
        return false;
    }
    //REGISTER THE TYPE

    Domain::registerType(mp_participant, &m_type);

    //CREATE THE PUBLISHER
    PublisherAttributes Wparam;
    Wparam.topic.topicKind = NO_KEY;
    Wparam.topic.topicDataType = "HelloWorld";
    Wparam.topic.topicName = "HelloWorldTopic";
    Wparam.topic.historyQos.kind = KEEP_LAST_HISTORY_QOS;
    Wparam.topic.historyQos.depth = 30;
    Wparam.topic.resourceLimitsQos.max_samples = 50;
    Wparam.topic.resourceLimitsQos.allocated_samples = 20;
    Wparam.times.heartbeatPeriod.seconds = 0;
    Wparam.times.heartbeatPeriod.nanosec = 10 * 1000;
    Wparam.qos.m_reliability.kind = RELIABLE_RELIABILITY_QOS;
//    mp_publisher = Domain::createPublisher(mp_participant, Wparam, (PublisherListener*)&m_listener);
    mp_publisher = Domain::createPublisher(mp_participant, Wparam, nullptr);
    if (mp_publisher == nullptr)
    {
        return false;
    }
#else
    DomainParticipantQos pqos;
    pqos.wire_protocol().builtin.discovery_config.discoveryProtocol = DiscoveryProtocol_t::SIMPLE;
    pqos.wire_protocol().builtin.discovery_config.use_SIMPLE_EndpointDiscoveryProtocol = true;
    pqos.wire_protocol().builtin.discovery_config.m_simpleEDP.use_PublicationReaderANDSubscriptionWriter = true;
    pqos.wire_protocol().builtin.discovery_config.m_simpleEDP.use_PublicationWriterANDSubscriptionReader = true;
    pqos.wire_protocol().builtin.discovery_config.leaseDuration = eprosima::fastrtps::c_TimeInfinite;
//    pqos.wire_protocol().builtin.discovery_config.leaseDuration = eprosima::fastrtps::c_TimeZero;
    pqos.name("PublisherParticipant");
    m_participant = DomainParticipantFactory::get_instance()->create_participant(0, pqos);

    if (m_participant == nullptr)
    {
        Logger_Error("Something went wrong while creating the Publisher Participant...");
        return false;
    }
    m_sampleType.register_type(m_participant);

    PublisherQos publisherQos;

    m_publisher = m_participant->create_publisher(PUBLISHER_QOS_DEFAULT);
    if (m_publisher == nullptr)
    {
        Logger_Error("Something went wrong while creating the Publisher...");
        return false;
    }
#endif
    return true;

}

HelloWorldPublisher::~HelloWorldPublisher()
{
//    Domain::removeParticipant(mp_participant);
    for (auto writer:m_Writer)
    {
        m_publisher->delete_datawriter(writer.second.second);
        m_participant->delete_topic(writer.second.first);
    }
    m_Writer.clear();
    m_participant->delete_publisher(m_publisher);
    DomainParticipantFactory::get_instance()->delete_participant(m_participant);
}

/*void HelloWorldPublisher::PubListener::onPublicationMatched(
        Publisher* *//*pub*//*,
        MatchingInfo& info)
{
    if (info.status == MATCHED_MATCHING)
    {
        n_matched++;
        firstConnected = true;
//        std::cout << "Publisher matched" << std::endl;
        Logger_Info("Publisher matched");
    }
    else
    {
        n_matched--;
//        std::cout << "Publisher unmatched" << std::endl;
        Logger_Info("Publisher unmatched");
    }
}*/

/*void HelloWorldPublisher::runThread(
        uint32_t samples,
        uint32_t sleep)
{
#if 0
    if (samples == 0)
    {
        while (!stop)
        {
            if (publish(false))
            {
//                std::cout << "Message: " << m_Hello.message() << " with index: " << m_Hello.index() << " SENT" <<
//                          std::endl;
                Logger_Info("[Message:{}, index:{}] SENT", m_Hello.message(), m_Hello.index());
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
        }
    }
    else
    {
        for (uint32_t i = 0; i < samples; ++i)
        {
            if (!publish())
            {
                --i;
            }
            else
            {
//                std::cout << "Message: " << m_Hello.message() << " with index: " << m_Hello.index() << " SENT" <<
//                          std::endl;
                Logger_Info("[Message:{}, index:{}] SENT", m_Hello.message(), m_Hello.index());
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(sleep));
        }
    }
#else
#endif
}*/

/*void HelloWorldPublisher::run(
        uint32_t samples,
        uint32_t sleep)
{
#if 0
    stop = false;
    std::thread thread(&HelloWorldPublisher::runThread, this, samples, sleep);
    if (samples == 0)
    {
//        std::cout << "Publisher running. Please press enter to stop the Publisher at any time." << std::endl;
        Logger_Info("Publisher running. Please press enter to stop the Publisher at any time.");
        std::cin.ignore();
        stop = true;
    }
    else
    {
//        std::cout << "Publisher running " << samples << " samples." << std::endl;
        Logger_Info("Publisher running. {} samples.", samples);
    }
    thread.join();
#else
#endif
}*/

//bool HelloWorldPublisher::publish(
//        bool waitForListener)
//{
//#if 0
//    if (m_listener.firstConnected || !waitForListener || m_listener.n_matched > 0)
//    {
//        m_Hello.index(m_Hello.index() + 1);
//        mp_publisher->write((void*)&m_Hello);
//        return true;
//    }
//#else
//#endif
//    return false;
//}

bool HelloWorldPublisher::WriterMessage(std::string topic_name, const HelloWorld& message)
{
    auto Channel = m_Writer.find(topic_name);
    if (Channel == m_Writer.end())
    {
        auto Topic = m_participant->create_topic(topic_name, m_sampleType.get_type_name(), TOPIC_QOS_DEFAULT);
        if (Topic == nullptr)
        {
            Logger_Error("Something went wrong while creating the Topic...");
            return false;
        }
        //Create DataWriter
        DataWriterQos wqos;
        wqos.endpoint().history_memory_policy = DYNAMIC_RESERVE_MEMORY_MODE;
//    wqos.history().kind = KEEP_ALL_HISTORY_QOS;
        wqos.history().kind = KEEP_LAST_HISTORY_QOS;
        wqos.durability().kind = TRANSIENT_LOCAL_DURABILITY_QOS; //如果 DataReader 在某个时间点之前未连接到 DataWriter，则该时间点之前写入的消息会被丢失
        wqos.reliability().kind = RELIABLE_RELIABILITY_QOS;
        wqos.history().depth =  20;
        wqos.resource_limits().max_samples = 50;
        wqos.resource_limits().max_instances = 1;
        wqos.resource_limits().max_samples_per_instance = 10;

        auto Writer = m_publisher->create_datawriter(Topic, wqos);
//        auto Writer = m_publisher->create_datawriter(Topic, DATAWRITER_QOS_DEFAULT);
        if (Writer == nullptr)
        {
            Logger_Error("Something went wrong while creating the Writer...");
            return false;
        }
        Writer->write((void*)&message);
        m_Writer.insert({topic_name, {Topic, Writer}});
    }
    else
    {
        Channel->second.second->write((void*)&message);
    }

    Logger_Info("Publishing message:[{} {}]", message.index(), message.message());


    return true;
}